/******************************************************************************
	This file is a part of the Snapshot Bioinformatics Project

	Copyright (C) 2012-2013 Jarom Schow

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/

#include <QtCore/QFile>
#include <QtCore/QTextStream>

#include "SnapshotGeneCollection.h"

#include "SqlServer.h"

const int	c_maxLength = 2048; // maximum sequence length

/**
**/
SnapshotSequence::SnapshotSequence() : m_bioentryId( -1 )
{
}

/**
**/
SnapshotSequence::SnapshotSequence( int bioentryId, const QString &accession, const Bio::SeqRange &location ) : m_bioentryId( bioentryId ),
	m_accession( accession ), m_location( location )
{
}

/**
**/
SnapshotSequence::~SnapshotSequence()
{
}

/**
**/
void SnapshotSequence::setBioentryId( int id )
{
	m_bioentryId = id;
}

/**
**/
int SnapshotSequence::getBioentryId() const
{
	return m_bioentryId;
}

/**
**/
void SnapshotSequence::setAccession( const QString &accession )
{
	m_accession = accession;
}

/**
**/
QString SnapshotSequence::getAccession() const
{
	return m_accession;
}

/**
**/
Bio::SeqRange SnapshotSequence::getLocation() const
{
	return m_location;
}

/**
**/
void SnapshotSequence::setLocation( const Bio::SeqRange &location )
{
	m_location = location;
}

/**
**/
SnapshotSequences::SnapshotSequences()
{
}

/**
**/
SnapshotSequences::SnapshotSequences( const QString &taxonName ) : m_taxonName( taxonName )
{
}

/**
**/
SnapshotSequences::~SnapshotSequences()
{
	qDeleteAll( m_sequences );
	m_sequences.clear();
}

/**
**/
void SnapshotSequences::setTaxonName( const QString &name )
{
	m_taxonName = name;
}

/**
**/
QString SnapshotSequences::getTaxonName() const
{
	return m_taxonName;
}

/**
**/
int SnapshotSequences::getNumSequences() const
{
	return m_sequences.count();
}

/**
**/
SnapshotSequence* SnapshotSequences::getSequence( int index ) const
{
	if( index < 0 || index >= m_sequences.count() )
		return NULL;

	return m_sequences[index];
}

/**
**/
void SnapshotSequences::addSequence( int bioentryId, const QString &accession, const Bio::SeqRange &range )
{
	m_sequences.append( new SnapshotSequence( bioentryId, accession, range ) );
}

/**
**/
QListIterator<SnapshotSequence*> SnapshotSequences::getSequenceIterator() const
{
	return QListIterator<SnapshotSequence*>( m_sequences );
}

/**
**/
SnapshotSequence* SnapshotSequences::chooseSequence( Bio::SqlServer &server ) const
{
	if( m_sequences.isEmpty() )
		return NULL;

	if( m_sequences.count() == 1 )
		return m_sequences[0];

	int					bestLength = 0;
	SnapshotSequence	*best = NULL;
	QString				sql = "SELECT length FROM biosequence WHERE bioentry_id = %1";
	QSqlQuery			query = server.getQuery();

	QListIterator<SnapshotSequence*>	seqIt( m_sequences );

	while( seqIt.hasNext() ){
		SnapshotSequence	*seq = seqIt.next();

		if( !server.exec( sql.arg( seq->getBioentryId() ) ) )
			continue;

		if( query.first() ){
			int	length = query.value( 0 ).toInt();

			if( length > bestLength && length < c_maxLength ){
				bestLength = length;
				best = seq;
			}
		}
	}

	if( best )
		return best;

	return m_sequences[0];
}

/**
**/
SnapshotGene::SnapshotGene( SnapshotGeneCollection *coll, const QString &name ) : m_coll( coll ),
	m_name( name ), m_selected( false )
{
}

/**
**/
SnapshotGene::~SnapshotGene()
{
	qDeleteAll( m_sequences.values() );
	m_sequences.clear();
}

/**
**/
void SnapshotGene::setName( const QString &name )
{
	m_name = name;
}

/**
**/
QString SnapshotGene::getName() const
{
	return m_name;
}

/**
**/
void SnapshotGene::setSelected( bool onOff )
{
	if( m_selected == onOff )
		return;

	m_selected = onOff;
	if( m_coll )
		m_coll->selectionChanged( this );
}

/**
**/
bool SnapshotGene::isSelected() const
{
	return m_selected;
}

/**
**/
int SnapshotGene::getNumSequences() const
{
	QHashIterator<int,SnapshotSequences*>	taxIt( m_sequences );

	int	seqCount = 0;

	while( taxIt.hasNext() ){
		taxIt.next();
		seqCount += taxIt.value()->getNumSequences();
	}

	return seqCount;
}

/**
**/
SnapshotSequences* SnapshotGene::getSequences( int taxonId )
{
	return m_sequences.value( taxonId, NULL );
}

/**
**/
void SnapshotGene::addSequence( const QString &taxonName, int taxonId, int bioentryId, const QString &accession, const Bio::SeqRange &range )
{
	if( !m_sequences.contains( taxonId ) )
		m_sequences[taxonId] = new SnapshotSequences( taxonName );

	m_sequences[taxonId]->addSequence( bioentryId, accession, range );
}

/**
**/
int SnapshotGene::getNumTaxa() const
{
	return m_sequences.count();
}

/**
**/
bool SnapshotGene::saveAs( const QString &filename, Bio::SqlServer &server, bool saveName, bool saveGI, bool saveGB ) const
{
	QFile	file( filename );

	if( !file.open( QIODevice::WriteOnly ) )
		return false;

	QTextStream	os( &file );

	QHashIterator<int,SnapshotSequences*>	taxIt( m_sequences );

	while( taxIt.hasNext() ){
		taxIt.next();

		bool				needSep = false;
		//int					taxId = taxIt.key();
		SnapshotSequences	*seqs = taxIt.value();
		SnapshotSequence	*seq = seqs->chooseSequence( server );
		
		os << '>';
		
		if( saveGI ){
			QString	gi = getGI( seq->getBioentryId(), server );

			if( !gi.isEmpty() ){
				os << "gi|" << gi;
				needSep = true;
			}
		}

		if( saveGB ){
			if( needSep )
				os << '|';
			else
				needSep = true;
			os << "gb|" << seq->getAccession();
		}

		if( saveName ){
			if( needSep )
				os << '|';
			os << seqs->getTaxonName();
		}

		os << '\n';

		QByteArray	sequence = getSequence( seq->getBioentryId(), server );

		while( sequence.length() > 70 ){
			os << sequence.left( 70 ) << '\n';
			sequence = sequence.mid( 70 );
		}

		if( !sequence.isEmpty() )
			os << sequence << '\n';
	}

	file.close();

	return true;
}

/**
**/
QHashIterator<int,SnapshotSequences*> SnapshotGene::getSequencesIterator() const
{
	return QHashIterator<int,SnapshotSequences*>( m_sequences );
}

/**
**/
QString SnapshotGene::getGI( int bioentryId, Bio::SqlServer &server ) const
{
	int		termId = server.getTermId( "gi", "Annotation Tags" );
	QString	sql = "SELECT value FROM bioentry_qualifier_value WHERE bioentry_id = " + QString::number( bioentryId ) +
					" AND term_id = " + QString::number( termId );

	if( !server.exec( sql ) )
		return QString();

	QSqlQuery	query = server.getQuery();

	if( !query.first() )
		return QString();

	return query.value( 0 ).toString();
}

/**
**/
QByteArray SnapshotGene::getSequence( int bioentryId, Bio::SqlServer &server ) const
{
	QString	sql = "SELECT seq FROM biosequence WHERE bioentry_id = " + QString::number( bioentryId );

	if( !server.exec( sql ) )
		return QByteArray();

	QSqlQuery	query = server.getQuery();

	if( !query.first() )
		return QByteArray();

	return query.value( 0 ).toByteArray();
}

/**
**/
SnapshotGeneCollection::SnapshotGeneCollection()
{
}

/**
**/
SnapshotGeneCollection::~SnapshotGeneCollection()
{
	clear();
}

/**
**/
int SnapshotGeneCollection::getNumGenes() const
{
	return m_genes.count();
}

/**
**/
int SnapshotGeneCollection::getNumSelectedGenes() const
{
	QListIterator<SnapshotGene*>	geneIt( m_genes );

	int	geneCount = 0;

	while( geneIt.hasNext() ){
		if( geneIt.next()->isSelected() )
			geneCount++;
	}

	return geneCount;
}

/**
**/
int SnapshotGeneCollection::getNumSequences() const
{
	QListIterator<SnapshotGene*>	geneIt( m_genes );

	int	seqCount = 0;

	while( geneIt.hasNext() ){
		seqCount += geneIt.next()->getNumSequences();
	}

	return seqCount;
}

/**
**/
SnapshotGene* SnapshotGeneCollection::getGene( int which ) const
{
	if( which < 0 || which >= m_genes.count() )
		return NULL;

	return m_genes[which];
}

/**
**/
SnapshotGene* SnapshotGeneCollection::getGene( const QString &name )
{
	QString	geneKey = name.toLower();

#ifdef GENE_NAME_HACK
	if( geneKey == "co1" || geneKey == "cox1" ){
		geneKey = "coi";
	}
	else if( geneKey == "co2" || geneKey == "cox2" ){
		geneKey = "coii";
	}
	else if( geneKey == "co3" || geneKey == "cox3" ){
		geneKey = "coiii";
	}
	else if( geneKey == "cyt b" ){
		geneKey = "cytb";
	}
#endif

	if( m_lookup.contains( geneKey ) )
		return m_lookup[geneKey];

	SnapshotGene	*gene = new SnapshotGene( this, name );

	m_genes.append( gene );
	m_lookup[geneKey] = gene;

	emit dataChanged();

	return gene;
}

/**
**/
QListIterator<SnapshotGene*> SnapshotGeneCollection::getGeneIterator() const
{
	return QListIterator<SnapshotGene*>( m_genes );
}

/**
**/
void SnapshotGeneCollection::clear()
{
	qDeleteAll( m_genes );
	m_genes.clear();
	m_lookup.clear();

	emit dataChanged();
}

/**
**/
void SnapshotGeneCollection::selectionChanged( SnapshotGene *gene )
{
	emit selectionChanged();
}
